home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 1994-12-08 | 41.6 KB | 1,130 lines | [ TEXT/R*ch]
C.S.M.P. Digest Sat, 16 Jan 93 Volume 2 : Issue 5 Today's Topics: To (?) Evan Shandev - my stream search source Free code: Packing up strings into a STR# Machine Icons International Number Formats midi help needed Background your Sound! Sound Input questions (real time usage) The Comp.Sys.Mac.Programmer Digest is moderated by Michael A. Kelly. The digest is a collection of article threads from the internet newsgroup comp.sys.mac.programmer. It is designed for people who read c.s.m.p. semi- regularly and want an archive of the discussions. If you don't know what a newsgroup is, you probably don't have access to it. Ask your systems administrator(s) for details. If you don't have access to news, there is no way that I know of for you to post articles to the group. Each issue of the digest contains one or more sets of articles (called threads), with each set corresponding to a 'discussion' of a particular subject. The articles are not edited; all articles included in this digest are in their original posted form (as received by our news server at cs.uoregon.edu). Article threads are not added to the digest until the last article added to the thread is at least one month old (this is to ensure that the thread is dead before adding it to the digest). Article threads that consist of only one message are generally not included in the digest. The entire digest is available for anonymous ftp from ftp.cs.uoregon.edu [128.223.8.8] in the directory /pub/mac/csmp-digest. Be sure to read the file /pub/mac/csmp-digest/README before downloading any files. The most recent issues are available from sumex-aim.stanford.edu [36.44.0.6] in the directory /info-mac/digest/csmp. If you don't have ftp capability, the sumex archive has a mail server; send a message with the text '$MACarch help' (no quotes) to LISTSERV@ricevm1.rice.edu for more information. The digest is also available via email. Just send a note saying that you want to be on the digest mailing list to mkelly@cs.uoregon.edu, and you will automatically receive each new issue as it is created. Sorry, back issues are not available through the mailing list. Send administrative mail to mkelly@cs.uoregon.edu. ------------------------------------------------------- From: urge@mcl.ucsb.edu (Scott Bronson) Subject: To (?) Evan Shandev - my stream search source Date: 14 Dec 92 05:04:11 GMT Sorry for butchering your name, but I forgot what it was exactly. I was forced to switch Email accounts, and in the move I lost my entire mail queue with your name and address safely tucked away. I apologize for all this. Anyway, I've decided to distribute this. It's simply a cool little algorithm to search an incoming stream. If you use this in one of your projects, I'd really like only one little thing in return: mail me and tell me how you use this source. My original letter follows. It first answers why I think that the CTB doesn't allow you to look at characters following a found CMSearch string, then gives my solution to this problem. - -------------------------------------- You're not doing anything wrong that I know of. The Comm Toolbox simply doesn't let you get at any text following your search. Here's my theory why (but I don't know, so don't quote me to anyone important): The connection tool transfers incoming text from the hardware buffer (4-byte SCC buffer, 64-byte serial buffer, or whatever) to its own larger buffer to hold until the application wants it. While performing this transfer, moving one character at a time, it updates its search strings in the same manner that I have coded below. When one of the strings gets found, it immediately calls your callback routine, without transfer- ring even another single character. When you think about it, this makes sense. You should not be able to get at characters further on in the buffer, as there is no definition to how many characters you would get. It would make things substantially harder for the connection tool, because it would always have to read x many more characters after the string was found, and if the buffer overflowed, the entire search string and all chars afterwards would have to be shifted around to make room. Not only that, but what standard would you make it? Eight characters more after the string found? Someone will need ten more. Okay then, ten. But what if someone needs fifty? A hundred? One K... So, best to let the application handle searching itself if it needs these special requirements. I think the search function was only implemented to allow comm programs to recognize automatic file transfer requests as demoed in the sample app that came with the CTB developer's kit. So, you're stuck without a standard and with no way to get at the next few characters after your search string. The only solution I see is for you to write your own. I did. Here's my version (you'll have to change all serial calls to be CTB calls, but other than that it's straightforward). Note that all of the strings used in this code are C strings, because they are far more buffer-friendly than Pascal strings. Also, some of this code was changed so I could send it to you, but it should (SHOULD--I'm not certian that I didn't add bugs when I edited it) remain bug-free. It is currently in use in a commercial application that I wrote, so I hope it will work without a flaw... // By Scott Bronson // // The only structure used... typedef struct searchrec { char *first, *cur; } searchrec; // We can search for this many strings maximum: #define kNumSearches 16 // Global variables used... short numsearches = 0; long gStringFound; searchrec search[kNumSearches]; // This is my actual idle loop. It should // be able to be used right out of the box. // // Idle process: // % read chars into inbuff // - strip the eighth bit // - check against current searches // - Strip newlines // - Write to OldLines buffer // - Put in our log file buffer // % flush log file buffer void WhatMeIdle( void ) { char inbuff[64], outbuff[64]; // 64-byte input buffer. The output buffer is used to store the 8th-bit // and newline stripped data that we will be displaying on screen. The // search algorithm will not be hurt at all by the disappearance of the // ouptut buffer. register char c; // indexes through each char in the input buffer. long count; // The number of characters read into inbuff. long j; // The number of characters in outbuff. short i, l; // Indexes for loops. OSErr err; // Stores possible errors returned by OS. err = SerGetBuf( inDriverRefNum, &count ); if( err == noErr && count > 0 ) { if( count > 64 ) count = 64; err = FSRead( inDriverRefNum, &count, inbuff ); // Now inbuff is filled with the next (count) characters // from the incoming data stream. if( err == noErr ) { // We'll loop through every character in the buffer... for( i=0,j=0; i<count; i++ ) { c = inbuff[i] & 0x7F; // Now c contains the next 7-bit character. // Here is the search algorithm. If there are // any searches pending, loop through them and // update the found character counts. if(numsearches) for( l=0; l<numsearches; l++ ) { if( c == *(search[l].cur++) ) { if( !*search[l].cur ) { // Ah! We found a string. Leave its index // in a global variable, clear all searches, // and finish reading the current buffer. // You can do whatever you want here. gStringFound = l+1; numsearches = 0; } } else search[l].cur = search[l].first; } // If c is a newline character, we're done with it. if( c == '\n' ) continue; // I performed some other actions here that were removed // for clarity. Now all we have to do is store c in the // output buffer so it will get written to our disk file. outbuff[j++] = c; } // end of for loop. // If we are storing the incoming data, then write it out. if( gLogToFile && j ) { FSWrite( gLogFileRef, &j, outbuff ); } } } // else no chars to be read. } // This is used to add a string to search for, same as CMAddSearch. // The string passed to AddSearch MUST remain untouched from the time // it is added until the time CheckStringFound returns a non-empty // string (meaning AddSearch doesn't make a copy of the string it is // passed--it just uses it in-place). void AddSearch( char *cs ) { if( numsearches < kNumSearches ) { search[numsearches].cur = search[numsearches].first = cs; numsearches++; } // else search buffer is full. } // This is called periodically to see if a string is found. It is passed // the address of a buffer large enough to hold the longest of all the // strings that are currently being searched for. The buffer will contain // the actual string found, or a 0-lenth string if none have been found yet. void CheckStringFound( char *ss ) { if( gStringFound ) { strcpy( ss, search[--gStringFound].first ); } else { ss[0] = 0; } } --------------------------- From: kurisuto@chopin.udel.edu (Sean J. Crist) Subject: Free code: Packing up strings into a STR# Date: 16 Dec 92 00:27:40 GMT Organization: University of Delaware I wrote the following code as a part of a parser, where I had to store a lot of strings in a table. Since storing strings as an array of Str255 (255 bytes per string) was very wasteful of memory, I wrote this code to pack strings up into a handle with almost no memory overhead. I believe that the structure I use is identical to that of STR# resources, but I haven't actually tried saving one of the resulting handles as a STR# and using GetIndString to see if it is completely identical or not. There's really no need to do this, since I've provided a routine for extracting the strings back out of the handle. I've tested the code pretty throroughly, but I'd appreciate hearing any bug reports. Comments are welcome. The code is in THINK Pascal 4.0. - --Kurisuto kurisuto@chopin.udel.edu unit STRpackaging; {By Sean Crist} {This code is for packing and unpacking strings in a compressed format like that of an STR#} {resource. The strings are kept in a handle. The first two bytes of this handle are the total } {number of strings. Following are the strings themselves. A Pascal string has the string length} {in the first byte followed by the characters themselves; if the length byte plus number of characters} {is an odd integer, we add an extra byte into the structure as packing.} interface {This function creates a new empty STR. You should pass it an unallocated handle; this routine} {will allocate the handle and will return it in the same argument that you passed. This function} {returns TRUE if the allocation was successful, FALSE if it couldn't allocate the memory (in which} {case you must gracefully deal with this failure).} function CreateNewSTR (var TheSTR: Handle): Boolean; {This function adds a new string to the end of a STR and returns its index. TheSTR is a string} {handle you created with CreateNewSTR; TheString is the Pascal string which you want to add} {to this handle. This function returns the index for the string in the list. If memory cannot be} {allocated to expand the handle, -1 is returned.} function AddToSTR (TheSTR: Handle; TheString: string): Integer; {This function searches for a given string and returns its index. It returns the index of the string;} {if it can't find a match for the string, it returns 0.} function FindInSTR (TheSTR: Handle; TheString: string): Integer; {This function, given an index, returns a string (much like GetIndString). If TheIndex is not a valid} {index, then the empty string '' is returned.} function ExtractSTR (TheSTR: Handle; TheIndex: Integer): Str255; implementation procedure doOSErr (WhichError: Integer); begin {You'd have to put whatever code you use to report an error here.} {My program puts up an alert with the error number as a ParamText argument.} {I've just included this here so that the unit will compile; this example is about} {packing up strings, not about reporting errors :-) } end; {About the following four routines: these routines are for compressing strings into} {an array with the same structure as a STR# resource. This is a way to save a lot} {of memory which would go wasted if we stored lists of strings as arrays of Str255.} function CreateNewSTR (var TheSTR: Handle): Boolean; var TheNewSTR: Handle; begin TheNewSTR := NewHandle(2); {Make a new handle the size of an integer.} if MemError <> 0 then {Check for MemError like good boys and girls} begin doOSErr(MemError); CreateNewStr := false; {Tell whoever called us that we've failed.} end else {The memory was successfully allocated, so go ahead.} begin CreateNewStr := true; {Tell whoever called us that we've succeeded.} TheSTR := TheNewSTR; StuffHex(TheNewSTR^, '0000'); {Initialize the number of strings to 0.} end; end; {The following routine makes odd numbers into even numbers by adding one if necessary.} function MakeEven (OddInteger: Integer): Integer; begin {If this is an even integer...} if OddInteger = ((OddInteger div 2) * 2) then {...then just return the number we were given...} MakeEven := OddInteger else {...but if this is an odd integer, add 1 to make it even.} MakeEven := OddInteger + 1; end; {This function adds a new string to the end of a STR and returns its index. If memory is} {insufficient, we return -1.} function AddToSTR; {(TheSTR: Handle, TheString: string): Integer;} var OldNumberOfStrings, NewNumberOfStrings: Integer; NewStringEvenLength, StringLength: Byte; counter, CurrentOffset: Integer; ScratchPtr: Ptr; OldSize: LongInt; OkSoFar: Boolean; begin OkSoFar := true; {Let's assume everything's going to be all right.} {Figure out how big the old handle and the new string are.} NewStringEvenLength := MakeEven(Length(TheString) + 1); {+1 for size byte} OldSize := GetHandleSize(TheSTR); {Set the size of the handle and check for errors.} SetHandleSize(TheSTR, OldSize + NewStringEvenLength); if MemError <> 0 then begin OkSoFar := false; doOSErr(MemError); end; {If everything is OK, then copy the new string into the handle.} if OkSoFar then begin HLock(TheSTR); {Figure out where the current end of the block is.} BlockMove(TheSTR^, @OldNumberOfStrings, 2); CurrentOffset := 2; if OldNumberOfStrings > 0 then for counter := 1 to OldNumberOfStrings do begin ScratchPtr := Ptr(LongInt(TheSTR^) + CurrentOffset); StringLength := ScratchPtr^; StringLength := MakeEven(StringLength + 1); CurrentOffset := CurrentOffset + StringLength; end; {Copy the string into there.} ScratchPtr := Ptr(LongInt(TheSTR^) + CurrentOffset); BlockMove(@TheString, ScratchPtr, NewStringEvenLength); {Update the count of strings.} NewNumberOfStrings := OldNumberOfStrings + 1; BlockMove(@NewNumberOfStrings, TheSTR^, 2); HUnlock(TheSTR); end; AddToSTR := NewNumberOfStrings; if not OkSoFar then AddToSTR := -1; end; {This function searches for a given string and returns its index. If we can't find it,} {we return 0.} function FindInSTR; {(TheSTR: Handle , TheString: string): Integer;} var Index, CurrentOffset, TopStrings: Integer; StringLength: Byte; CheckString: Str255; done, foundIt: Boolean; ScratchPtr: Ptr; begin Index := 0; BlockMove(TheSTR^, @TopStrings, 2); {Get the number of strings.} CurrentOffset := 2; done := false; foundIt := false; if TopStrings > 0 then {Loop through the strings until we find a match or until we've gone through them all.} while not done do begin Index := Index + 1; ScratchPtr := Ptr(LongInt(TheSTR^) + CurrentOffset); StringLength := ScratchPtr^; StringLength := MakeEven(StringLength + 1); BlockMove(ScratchPtr, @CheckString, StringLength); if EqualString(CheckString, TheString, false, true) then begin done := true; foundIt := true; end else begin CurrentOffset := CurrentOffset + StringLength; end; if Index = TopStrings then done := true; end; if FoundIt then FindInSTR := Index else FindInStr := 0; end; {This function, given an index, returns a string. If the index is out of range, we} {return an empty string.} function ExtractSTR; {(TheSTR: Handle, TheIndex: Integer): Str255;} var CurrentOffset, TopStrings, counter: Integer; StringLength: Byte; TheString: Str255; ScratchPtr: Ptr; begin BlockMove(TheSTR^, @TopStrings, 2); CurrentOffset := 2; TheString := ''; if (TopStrings > 0) and (TheIndex <= TopStrings) then begin for counter := 1 to TheIndex - 1 do begin ScratchPtr := Ptr(LongInt(TheSTR^) + CurrentOffset); StringLength := ScratchPtr^; StringLength := MakeEven(StringLength + 1); CurrentOffset := CurrentOffset + StringLength; end; ScratchPtr := Ptr(LongInt(TheSTR^) + CurrentOffset); StringLength := ScratchPtr^; StringLength := MakeEven(StringLength + 1); BlockMove(ScratchPtr, @TheString, StringLength); end; ExtractSTR := TheString; end; end. --------------------------- From: GREMICF@YaleVM.YCC.Yale.Edu (M. David Greenspon) Subject: Machine Icons Date: 11 Dec 92 16:32:39 GMT Organization: Yale University When you choose "About This Macintosh" from the Apple Menu in the finder, you get, among other things, an icon that looks like your machine. These icons are stored as ICN#s in the System file. When I call Gestalt with a selector of gestaltMachineIcon on my PB160, I get 49031; sure enough, ICN# 49031 (= -16505) in the System file looks like a PowerBook. Is there a list anywhere of Machine Icon numbers for _all_ Macs? I don't have access to every kind of Mac ever made to go around running my program on. In general, most of the icons do (sort of) look like the Macs I think they're supposed to represent, but in some cases I'm not sure exactly which is which. I'd like to be sure that I really am showing the "official" ones in all cases. The newest list of undocumented Gestalt stuff posted here doesn't say anything about this. I can't find a Tech Note about it. I'm doing this because I'd like to determine various things about the system I'm on: for example, if I get an icon that looks like a Quadra, I'll assume the presence of an FPU. Just kidding! ;-) :-] (I can just see the frantic letters pouring in from people who were too aghast to read further... ;~> ) I just want to display info about various Macs, and I thought it would be nice to display the icon. Thanks in advance for any info. - --David +++++++++++++++++++++++++++ From: Michael Hecht <Michael_Hecht@mac.sas.com> Date: Mon, 14 Dec 1992 15:37:07 GMT Organization: SAS Institute Inc. In article <168BAA257.GREMICF@YaleVM.YCC.Yale.Edu> M. David Greenspon, GREMICF@YaleVM.YCC.Yale.Edu writes: >I'm doing this because I'd like to determine various things about the >system I'm on: for example, if I get an icon that looks like a Quadra, >I'll assume the presence of an FPU. Hah! Good one. Had me going there for a few seconds. :-) Well, a little tracing with MacsBug reveals the following table that maps machine type to icon (for System 7.0.1*, anyway): Machine Type Machine Icon ============ ============ unknown (0) 3 gestaltClassic (1) -16482 gestaltMacXL (2) 3 gestaltMac512KE (3) -16482 gestaltMacPlus (4) -16482 gestaltMacSE (5) -16483 gestaltMacII (6) -16486 gestaltMacIIx (7) -16486 gestaltMacIIcx (8) -16485 gestaltMacSE030 (9) -16483 gestaltPortable (10) -16484 gestaltMacIIci (11) -16485 (12) 3 gestaltMacIIfx (13) -16486 (14) 3 (15) 3 (16) 3 gestaltMacClassic (17) -16482 gestaltMacIIsi (18) -16502 gestaltMacLC (19) -16503 (20) -16504 (21) -16505 (22) -16507 (23) -16482 (24) -16508 (25) -16505 (26) -16504 (27) -16482 [Sorry, I don't have the machine names of the types above 19 handy at the moment.] Of course, using this table as such has loads of problems: * Apple could change the icon numbers on you. * When new machine types are added, you'll never know about them. To solve these problems, I suggest you take advantage of the fact that the machineIcon Gestalt selector calls the machineType gestalt selector to get the index into the above table. Use ReplaceGestalt to temporarily substitute the 'mach' selector with your own routine that will hand back whatever selector you want. Then, call Gestalt for the 'micn' repeatedly, and have your 'mach' function substitute a different machineType for each call. You'll have to write your replacement Gestalt selector as a stand-alone code segment that loads into the System Heap. ReplaceGestalt complains, otherwise. You can store the fake machine selector somewhere near the beginning of it from your app, and just have the routine return it. How do you know how many times to call? Fetch the kMachineNameStrID string list resource and see how many strings are in it! Don't forget to replace the original 'mach' routine when you're done. Good luck! - --Michael PS: What are you writing? ======================================================================= Michael P. Hecht | Internet: Michael_Hecht@mac.sas.com SAS Institute Inc.; Cary, NC USA | AppleLink: SAS.HECHT --------------------------- From: kamprath@space-grant.sprl.umich.edu (Michael F. Kamprath) Subject: International Number Formats Date: 14 Dec 92 01:46:42 GMT Organization: University of Michigan, Aerospace Engineering I'm trying to decipher IM VI with respect to how to convert a floating point number into a string to be displayed in the correct format (as set by the Nums&Date control panel) and vice versa (that is, strings --> numbers). I'm not having much luck. Can anybody explain to me how to do this? Thanks. ====================================================================== |Michael F. Kamprath | The University of Michigan | | kamprath@space-grant.sprl.umich.edu | Aerospace Engineering | | kamprath@engin.umich.edu | Graduate Student | ====================================================================== +++++++++++++++++++++++++++ From: wysocki@netcom.com (Chris Wysocki) Date: 15 Dec 92 05:02:12 GMT Organization: Connectix Corporation, San Mateo, CA In article <4TS=Cj-@engin.umich.edu> Michael F. Kamprath <kamprath@space-grant.sprl.umich.edu> writes: >I'm trying to decipher IM VI with respect to how to convert a floating point >number into a string to be displayed in the correct format (as set by the Nums >&Date control panel) and vice versa (that is, strings --> numbers). Use the Script Manager routine Str2Format to convert a format string (such as "#,###,###;-#,###,###") to a canonical NumFormatString (which describes the format string in a script-independent manner), then use FormatX2Str and FormatStr2X to convert between 80-bit SANE extendeds and formatted strings. You can obtain the necessary NumberParts table from the 'itl4' resource via: Handle itl4H; NumberParts parts; itl4H = IUGetIntl(4); FailNILRes(itl4H); parts = *(NumberParts *)(*itl4H + (**(Itl4Handle)itl4H).defPartsOffset); The one trick is that you need to save the canonical NumFormatString returned by Str2Format in some way (e.g. in a resource), since Str2Format will interpret the format string differently depending on the current system environment (active script system, Numbers and Dates control panel settings, etc.) To avoid this, write a throw-away program that calls Str2Format and saves the resulting NumFormatString in a resource. Then, when you need to display a formatted number in your main program, get the saved NumFormatString resource, lock it down, and pass the dereferenced handle to FormatX2Str. Complete details about Str2Format, FormatX2Str and FormatStr2X can be found in the Worldwide Guide to System Software stack, available on the developer CDs. Chris. - -- - ------------------------------------------------------------------------------ Chris Wysocki Internet: wysocki@netcom.com Software Engineer America Online: AFA ChrisW Connectix Corporation CompuServe: 72010,1140 --------------------------- From: rmaag@iiic.ethz.ch (Rolf Maag) Subject: midi help needed Organization: Dept. Informatik, Swiss Federal Institute of Technology (ETH), Zurich, CH Date: Mon, 14 Dec 1992 15:29:50 GMT hi there, i am writing a midi-sequencing program with think c 4.0. i don't want to write my own midi-device driver, so can anyone tell me where i can get one? or maybe someone has a nice include file? anyway, thanx a lot and have fun.. email to rmaag@iiic.ethz.ch +++++++++++++++++++++++++++ From: steve@oceania.com Organization: Oceania Health Care Systems Date: Mon, 14 Dec 1992 21:18:50 GMT Rolf Maag writes > i am writing a midi-sequencing program with think c 4.0. i don't > want to write my own midi-device driver, so can anyone tell me where > i can get one? or maybe someone has a nice include file? anyway, > thanx a lot and have fun.. I don't think a 'nice include file' will do the trick. MIDI is pretty complex, being so time sensitive and all. I have been using Apple's MIDIManager for about a year. It works quite well and only cost me $35. It's a system extension that offers MIDI I/O functions to any program that registers itself with the manager. You use an application that is included called PatchBay to connect MIDI input and output devices/programs. So far I have written one complete application with it and am on to my second. It has proven to be relatively simple and easy to use. If anyone else is using it, please email me. I'd like to know if anyone would be able to use the programs I write with it if I released them as freeware. Good luck, Steve - -- Steve Dakin | Oceania Health Care Systems | That one deserves a ... WOW! Palo Alto, CA (415) 322-0127 | steve@oceania.com (NeXT mail) | --------------------------- From: Daniel_R._Sandler@uu0570.foggybottom.com Organization: Foggy Bottom / Washington, D.C. Date: Wed, 09 Dec 1992 17:37:22 EST Subject: Background your Sound! Okay, I've asked this before, and I haven't gotten an answer yet. This means there are 3 possibilities: either a) no one knoes how to do it (esp. not on my Classic); b) No one is responding; or c) whenevr you DO respond I miss the message because I do not log in soon enough and the message is flushed out of my BBS' temporary holding place for Inet newsgroup messages. OK,ok, to the point: How do I play sound while something else is ahppening, like so many games for the Mac, and even HyperCard? If my games have to stop and pause to let a sound play, it really ruins the hole effect. Thanks in advance. dan 95dsandler@vax.mbhs.edu Daniel_R._Sandler@uu0570.foggybottom.com Foggy Bottom - your FirstClass Mac connection in Washington, DC +++++++++++++++++++++++++++ From: werner@dewey.soe.berkeley.edu (John Werner) Date: 10 Dec 1992 17:42:07 GMT Organization: School of Education, U.C. Berkeley In article <1992Dec09.173722.69189@uu0570.foggybottom.com> Daniel_R._Sandler@uu0570.foggybottom.com writes: >OK,ok, to the point: How do I play sound while something else is ahppening, >like so many games for the Mac, and even HyperCard? If my games have to stop >and pause to let a sound play, it really ruins the hole effect. It's not that hard. Use SndPlay, with the last "async" parameter set to TRUE. The catch is that this only works if you're using your own sound channel. So the code looks something like this: SndChannelPtr myChannel; SndNewChannel(&myChannel, sampledSynth, 0, NULL); /* Do once at startup */ . . SndPlay(myChannel, mySound, TRUE); . . SndDisposeChannel(myChannel, TRUE); /* Do this once at exit */ - -- John Werner werner@soe.berkeley.edu UC Berkeley School of Education 510-642-9651 +++++++++++++++++++++++++++ From: k044477@hobbes.kzoo.edu (Jamie R. McCarthy) Organization: Kalamazoo College Date: Thu, 10 Dec 1992 21:03:23 GMT werner@dewey.soe.berkeley.edu (John Werner) writes: >Daniel_R._Sandler@uu0570.foggybottom.com writes: >> >>OK,ok, to the point: How do I play sound while something else is ahppening, > >It's not that hard. Famous last words. :-) :-) >Use SndPlay, with the last "async" parameter set >to TRUE. The catch is that this only works if you're using your own >sound channel. So the code looks something like this: > >SndChannelPtr myChannel; Don't forget to set myChannel to NULL here. Yeah, yeah, I know, this shouldn't be a problem. But you never know. If memory management concerns you, you'll want to be sure the SndChannel gets allocated in the right place. If you let SndNewChannel do it for you, it'll essentially make a NewPtr() call. By doing this yourself earlier in the program when the nonrelocatable block'll do less damage, or by making it a (locked!) handle that's moved hi, you can alleviate any problems. Of course, the method given here will still work, if the fragmentation is not a concern. >SndNewChannel(&myChannel, sampledSynth, 0, NULL); /* Do once at startup */ > . > . >SndPlay(myChannel, mySound, TRUE); > . > . >SndDisposeChannel(myChannel, TRUE); /* Do this once at exit */ If you allocate the memory yourself, you would instead write (for example): SndChannel **mySndChannelHndl; mySndChannelHndl = (SndChannel**) NewHandleClear(sizeof(SndChannel)); // the "Clear" is _very_ important! (**mySndChannelHndl).qLength = stdQLength; MoveHHi((Handle) mySndChannelHndl); HLock((Handle) mySndChannelHndl); ...later on... SndNewChannel(*mySndChannelHndl, sampledSynth, initMono, NULL); SndPlay(*mySndChannelHndl, mySoundRsrc, TRUE); SndDisposeChannel(*mySndChannelHndl, TRUE); ...and at app shutdown... DisposHandle((Handle) mySndChannelHndl); Two caveats. (1) SndChannel's, like GrafPort's, must not move around (the system stores the list of them as a linked list of pointers). If you do the handle thing, make sure it's locked. (2) The Sound Manager compares the channel pointer that you pass it to its list, in an effort to find it. (QuickDraw probably does the same thing with GrafPtr's.) So don't do anything silly like make the handle purgeable or (worse) unlocked--if you're in 24-bit mode, the hi byte of the master pointer will change, and the Sound Manager will suddenly not recognize it. Just lock it when it's created, leave it alone while you're using it, and you'll avoid both these problems. - -- Jamie McCarthy Internet: k044477@kzoo.edu AppleLink: j.mccarthy "Apple developers will still have access to System 7.1 through the monthly Developer CD series, but they may use it only for testing and development purposes." - AppleDirect, Nov/Dec 92 +++++++++++++++++++++++++++ From: ingemar@isy.liu.se (Ingemar Ragnemalm) Date: 11 Dec 92 12:38:53 GMT Organization: Dept of EE, University of Linkoping werner@dewey.soe.berkeley.edu (John Werner) writes: >In article <1992Dec09.173722.69189@uu0570.foggybottom.com> Daniel_R._Sandler@uu0570.foggybottom.com writes: >>OK,ok, to the point: How do I play sound while something else is ahppening, >>like so many games for the Mac, and even HyperCard? If my games have to stop >>and pause to let a sound play, it really ruins the hole effect. >It's not that hard. Use SndPlay, with the last "async" parameter set >to TRUE. The catch is that this only works if you're using your own >sound channel. So the code looks something like this: (code deleted) Speaking of asynchronous sound, what are the "rules" about several channels open at once? I have tried to make two channels for sampled sounds (on an LC), to play a sequence of notes on one of them (with a snd installed as "instrument") and various sound effects on the other. I get "not enough hardware". What information have I missed? Can't I have two sampled sound channels, or can't I use one of them for melodies? What can be done and what can't when using several channels? - -- Ingemar Ragnemalm Dept. of Electrical Engineering ...!uunet!mcvax!enea!rainier!ingemar .. University of Linkoping, Sweden ingemar@isy.liu.se +++++++++++++++++++++++++++ From: k044477@hobbes.kzoo.edu (Jamie R. McCarthy) Date: 11 Dec 92 21:53:00 GMT Organization: Kalamazoo College ingemar@isy.liu.se (Ingemar Ragnemalm) writes: > >Speaking of asynchronous sound, what are the "rules" about several channels >open at once? I have tried to make two channels for sampled sounds on an >LC... I get "not enough hardware". I've heard this from other people, but I'll personally affirm that one can get two channels open on an LC. Maybe if you showed us your code? On my IIci, one "good-quality" channel takes about 18% of the CPU time. I think it's a trifle over 30% on the LC. "Good-quality" here means with both linear interpolation and drop-sample conversion. Jim Reekes has indicated, however, that one of these is currently unimplemented (drop-sample conversion, I think). He's also indicated that the CPU-time values returned by the S.M. are rather unreliable, so take the previous paragraph with a grain of salt. - -- Jamie McCarthy Internet: k044477@kzoo.edu AppleLink: j.mccarthy "Apple developers will still have access to System 7.1 through the monthly Developer CD series, but they may use it only for testing and development purposes." - AppleDirect, Nov/Dec 92 +++++++++++++++++++++++++++ From: edw@distant.uucp (Ed Watkeys) Date: 11 Dec 92 23:32:17 GMT Organization: Distant Software In article <ingemar.724077533@isy> (comp.sys.mac.programmer), ingemar@isy.liu.se (Ingemar Ragnemalm) writes: > werner@dewey.soe.berkeley.edu (John Werner) writes: > > >In article <1992Dec09.173722.69189@uu0570.foggybottom.com> Daniel_R._Sandler@uu0570.foggybottom.com writes: > >>OK,ok, to the point: How do I play sound while something else is ahppening, > >>like so many games for the Mac, and even HyperCard? If my games have to stop > >>and pause to let a sound play, it really ruins the hole effect. > > >It's not that hard. Use SndPlay, with the last "async" parameter set > >to TRUE. The catch is that this only works if you're using your own > >sound channel. So the code looks something like this: > > (code deleted) > > Speaking of asynchronous sound, what are the "rules" about several channels > open at once? I have tried to make two channels for sampled sounds (on an > LC), to play a sequence of notes on one of them (with a snd installed as > "instrument") and various sound effects on the other. I get "not enough > hardware". What information have I missed? Can't I have two sampled sound > channels, or can't I use one of them for melodies? > > What can be done and what can't when using several channels? I think this is addressed in the Sound Manager chapter of IM VI. If I recall, you get a 'not enough hardware' error if you ask for too many channels with too high a quality. This is hardware dependent, of course. :) Ed > Ingemar Ragnemalm - -- Edwin H. Watkeys III edw@distant.uucp Distant Software dsinc!jabber!distant!edw +1 215 387 7971 edw%distant@bts.com +++++++++++++++++++++++++++ From: ldo@waikato.ac.nz (Lawrence D'Oliveiro, Waikato University) Date: 14 Dec 92 17:55:44 +1300 Organization: University of Waikato, Hamilton, New Zealand In article <1992Dec11.215300.2937@hobbes.kzoo.edu>, k044477@hobbes.kzoo.edu (Jamie R. McCarthy) writes: > ingemar@isy.liu.se (Ingemar Ragnemalm) writes: >> >>Speaking of asynchronous sound, what are the "rules" about several channels >>open at once? I have tried to make two channels for sampled sounds on an >>LC... I get "not enough hardware". > > I've heard this from other people, but I'll personally affirm that one > can get two channels open on an LC. Maybe if you showed us your code? > > On my IIci, one "good-quality" channel takes about 18% of the CPU > time. I think it's a trifle over 30% on the LC. It also depends on what you ask the channel to do. Playing a single channel of MACE compressed sound takes 48% of the CPU on my LC (or so the Sound Manager says). I can indeed play two such channels at once, and *everything* else on the machine (particularly screen drawing!) slows to a c-r-a-w-l. With uncompressed sound, I have had 3 channels open at once with no problem, sometimes even 4. I think getting the 4th channel had something to do with not requesting sample-rate conversion, but it's been a while since I experimented with it. I remember getting that 30% figure when opening the first channel, but as I recall subsequent channels only increased the utilization by something like 18% each. I managed to fool the Sound Manager once (I must figure out how to do it again) and open enough channels to take the CPU utilization over 100%. Things sort of ran, but it wasn't a great deal of fun. :-) Lawrence D'Oliveiro fone: +64-7-856-2889 Computer Services Dept fax: +64-7-838-4066 University of Waikato electric mail: ldo@waikato.ac.nz Hamilton, New Zealand 37^ 47' 26" S, 175^ 19' 7" E, GMT+13:00 --------------------------- From: bpb9204@tamsun.tamu.edu (Brent Burton) Subject: Sound Input questions (real time usage) Date: 13 Dec 92 18:04:21 GMT Organization: Texas A&M Univ., Inc. In short, I'm writing a program to draw the shape of the sound coming in on the built-in device. However, I'm new to using the new Sound Manager and I don't know how to properly approach it. Think of my program as simply drawing a bar, which has a height that is proportional to the sound level coming in, like a meter. In IM-6, p 22-95, (talking about the sound parameter blocks), they say that, "if the bufferPtr field contains NIL, then the count, milliseconds, and bufferLength fields are ignored, and the recording continues indefinitely until you cal SPBStopRecording. If bufferPtr is NIL, the audio data is not saved anywhere; this feature is useful only if you want to do something in your interrupt routine and do not want to save the audio data." It says the data is not saved anywhere, but since I'm not interested in saving any portion of the input stream (I want the current value only), is there a location I can look to get the current digitized value? Do I have to do this through the interrupt routine? Since my program is effectively a level meter, I just need the current values. Another approach I was thinking about was using a small buffer, like << 100 bytes. Since the buffer is small, the value contained in it would be relatively close the the current value. Is this feasible? Anyway, if you have some ideas, please let me know. thanks, - -Brent - -- +-------------------------+ | Brent Burton N5VMG | | bpb9204@tamsun.tamu.edu | +-------------------------+ +++++++++++++++++++++++++++ From: bpb9204@tamsun.tamu.edu (Brent Burton) Date: 15 Dec 92 06:24:33 GMT Organization: Texas A&M Univ., Inc. bpb9204@tamsun.tamu.edu (Brent Burton) writes: | |Think of my program as simply drawing a bar, which has a height |that is proportional to the sound level coming in, like a meter. | |Since my program is effectively a level meter, I just need the current |values. Another approach I was thinking about was using a small buffer, |like << 100 bytes. Since the buffer is small, the value contained in |it would be relatively close the the current value. Is this feasible? I have learned a lot about the Sound Manager during the last few days, and I have solved my problem. Essentially, what you would do is to call a routine, InitSound() for example, to open the driver and begin recording. You would then poll the device to determine the current sound level whenever you needed to know the sound level. Finally, when you are done polling/using the device, you call a routine such as KillSound() to stop recording and close the device. This turned out to be simple, and brief pseudocode follows for the routines: (The code is from memory, so be sure to check, for instance, the parameter block's field names) - ------------------------------------ #include <SoundInput.h> long sndRefNum; #define kAsynch TRUE InitSound() { OSErr e; SPB s; e = SPBOpenDevice( NULL, siWritePermission, &sndRefNum); if ( e != noErr) { /** initialize the parameter block - fill in all fields - see IM 6 **/ s.count =0; s.milliseconds=0; s.buffPtr = NULL; s.buffLen = 0; s.completionRoutine=NULL; s.interruptRoutine=NULL; s.inRefNum = sndRefNum; /* ... */ e = SPBRecord( &s, kAsynch); /* record asynchronously */ if (e != noErr) { /* error */ } } else { /* error */ } } /* InitSound() */ /**** After InitSound() is called, you can poll the device to get ***** various characteristics about the recording. ** Call SPBGetRecordingStatus() (see inside mac 6) to get ** recording time so far, remaining, the current sound level on the ** input, etc. *****/ /** Once your are done with polling, call KillSound() to clean up **/ KillSound() { OSErr e; e = SPBStopRecording( sndRefNum); e = SPBCloseDevice( sndRefNum); sndRefNum=0; } /* killSound() */ - ---------------------------- Hope this can help someone else. It works for me. - -Brent - -- +-------------------------+ | Brent Burton N5VMG | | bpb9204@tamsun.tamu.edu | +-------------------------+ --------------------------- End of C.S.M.P. Digest **********************